/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *  filename:   filemanagement.c/filemanagement
 *  copyright:  changhong electric co., ltd
 *  breif:      实现了filemanagement.h中声明的函数，即文件管理模块的主要功能
 *              TODO 对因权限等问题引发的操作失败采取相应策略
 *  ------------------------------------------------------------------------------
 *  Rev     Date        Author          Comments
 *  1       2009-10-10  qingsong.li     Create
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "filemanagement.h"

/* 打印出错信息并退出函数 */
void err_msg(const char *fmt, ...)
{
        va_list ap;
        va_start(ap, fmt);
        err_doit(1, errno, fmt, ap);
        va_end(ap);
}

/* 打印相关出错信息并返回调用函数 */
void err_doit(int errnoflag, int error, const char *fmt, va_list ap)
{
        char buf[PATH_MAX];
        vsnprintf(buf, PATH_MAX, fmt, ap);

        if (errnoflag)
                snprintf(buf + strlen(buf), PATH_MAX - strlen(buf), ": %s",
                                strerror(error));
        strcat(buf, "\n");
        fflush(stdout);
        fputs(buf, stderr);
        fflush(NULL);
}

/* 判断文件或文件夹是否存在。是则返回1；否则返回0 */
int path_exist(char *pathname)
{
        if (access(pathname, F_OK))
                return 0;
        else
                return 1;
}

/* 判断一个文件是否为音乐文件，是则返回1,否则返回0，
 * 以文件后缀名判断，只支持mp3, wma两种格式 */
int mus_file(char *pathname)
{
        char *ptr = strrchr(pathname, '.');
        if (ptr == NULL)
                return 0;

        if (!strncasecmp(ptr, ".mp3", 4) || !strncasecmp(ptr, ".wma", 4))
                return 1;
        else
                return 0;
}

/* 判断一个文件是否为视频文件，是否返回1, 否则返回0，
 * 以文件后缀名判断，目前只支持avi, wmv, mpg, rmvb, rm, mkv六种格式 */
int mov_file(char *pathname)
{
        char *ptr = strrchr(pathname, '.');
        if (ptr == NULL)
                return 0;

        if (!strncasecmp(ptr, ".avi", 4) || !strncasecmp(ptr, ".wmv", 4)
                        || !strncasecmp(ptr, ".mpg", 4) || !strncasecmp(ptr,
                        ".rmvb", 5) || !strncasecmp(ptr, ".rm", 3)
                        || !strncasecmp(ptr, ".mkv", 4))
                return 1;
        else
                return 0;
}
/* 判断一个文件是否为图片文件，是否返回1, 否则返回0，
 * 以文件后缀名判断，目前只支持gif, jpg, jpeg, png四种格式 */
int pic_file(char *pathname)
{
        char *ptr = strrchr(pathname, '.');
        if (ptr == NULL)
                return 0;

        if (!strncasecmp(ptr, ".gif", 4) || !strncasecmp(ptr, ".jpg", 4)
                        || !strncasecmp(ptr, ".jpeg", 5) || !strncasecmp(ptr,
                        ".png", 4))

                return 1;
        else
                return 0;
}

/* 判断一个文件是否为文本文件，是否返回1, 否则返回0，
 * 以文件后缀名判断，目前只支持txt一种格式 */
int txt_file(char *pathname)
{
        char *ptr = strrchr(pathname, '.');
        if (ptr == NULL)
                return 0;

        if (!strncasecmp(ptr, ".txt", 4))

                return 1;
        else
                return 0;
}

/* 判断一个文件是否为软链接，是则否返回1, 否则返回0 */
int lnk_file(char *pathname)
{
        struct stat statbuf;
        if (lstat(pathname, &statbuf) < 0)
        {
                err_msg("in lnk_file(), lstat error, %s", pathname);
                return 0;
        }

        if (S_ISLNK(statbuf.st_mode))
                return 1;
        else
                return 0;
}

/* 判断文件类型，此处将文件分七类，音乐文件(TYPE_MUS)，视频文件(TYPE_MOV)，
 * 图片文件(TYPE_PIC)，文本文件(TYPE_TXT)，其他文件(TYPE_OTH)，软链接(TYPE_SL)
 * 及文件夹(TYPE_D)，返回相应的文件类型，出错返回-1 */
int file_type(char *pathname)
{
        struct stat statbuf;
        if (lstat(pathname, &statbuf) < 0)
        {
                err_msg("in file_type(), lstat error, %s", pathname);
                return -1;
        }

        if (lnk_file(pathname))
                return TYPE_SL;
        else if (S_ISDIR(statbuf.st_mode))
                return TYPE_D;
        else if (S_ISREG(statbuf.st_mode))
        {
                if (mov_file(pathname))
                        return TYPE_MOV;
                else if (mus_file(pathname))
                        return TYPE_MUS;
                else if (pic_file(pathname))
                        return TYPE_PIC;
                else if (txt_file(pathname))
                        return TYPE_TXT;
                else
                        return TYPE_OTH;
        }

        return -1;
}

/* 判断pathsrc和pathdest是否在同一设备上，是则返回1,否则返回0,出错返回-1
 * pathdest不必存在，但pathsrc必须存在，否则返回0 */
int on_same_device(char *pathsrc, char *pathdest)
{
        int create_flag = 0;
        struct stat srcbuf, destbuf;
        if (!path_exist(pathdest))
        {
                if (creat(pathdest, 0644) < 0)
                {
                        err_msg("in on_same_device(), creat error, %s",
                                        pathdest);
                        return -1;
                }
                create_flag = 1;
        }

        if (lstat(pathsrc, &srcbuf) < 0)
        {
                err_msg("in on_same_device(), lstat error, %s", pathsrc);
                return -1;
        }
        else if (lstat(pathdest, &destbuf) < 0)
        {
                err_msg("in on_same_device(), lstat error, %s", pathdest);
                return -1;
        }

        if (major(srcbuf.st_dev) != major(destbuf.st_dev)
                        || minor(srcbuf.st_dev) != minor(destbuf.st_dev))
        {
                if (create_flag)
                        unlink(pathdest);
                return 0;
        }
        else
        {
                if (create_flag)
                        unlink(pathdest);
                return 1;
        }
}

/* 判断subdir是否为pardir的子目录，两者均可不存在，是则返回1,否则返回0 */
int subdir(char *subdir, char *pardir)
{
        int i;

        for (i = 0; i < strlen(pardir) - 1; ++i)
                if (pardir[i] != subdir[i])
                        return 0;
        if (strncmp(subdir, pardir, PATH_MAX) == 0)
                return 0;

        return 1;
}

/* 判断list中的各目录相对于act是否有效，若是返回1,否则返回0*/
int pathvalid(PathList *list, char *pathdest, int act)
{
        if (act < 0 || act > 2 || list == NULL || list->length == 0)
                return 0;

        int i;
        char *ptr, *fullptr, fullpath[PATH_MAX];

        if (act == ACT_DEL || act == ACT_CUT)
                for (i = 0; i < list->length; ++i)
                        if (access(list->pathname[i], W_OK))
                        {
                                err_msg("%s", list->pathname[i]);
                                return 0;
                        }
        if (act == ACT_CUT || act == ACT_CPY)
                if (access(pathdest, W_OK) || !path_exist(pathdest))
                {
                        err_msg("%s", pathdest);
                        return 0;
                }

        strncpy(fullpath, pathdest, PATH_MAX);
        fullptr = fullpath + strlen(fullpath);

        if (act == ACT_DEL)
                return 1;
        else
        {
                for (i = 0; i < list->length; ++i)
                {
                        ptr = strrchr(list->pathname[i], '/');
                        if (PATH_MAX - (strlen(ptr) + strlen(pathdest)) > 0)
                                strcpy(fullptr, ptr);
                        else
                        {
                                err_msg("pathname is too long");
                                return 0;
                        }
                        if (subdir(fullpath, list->pathname[i]) || strncmp(
                                        fullpath, list->pathname[i], PATH_MAX)
                                        == 0)
                                return 0;
                }
        }

        return 1;
}

/* 对线线表进行初始化，初始化后线性表的长度为0,可容纳LIST_SIZE个元素，
 * 成功则返回0,否则返回-1 */
int init_list(PathList *list)
{
        if (list == NULL)
                return -1;

        list->pathname = calloc(LIST_SIZE * PATH_MAX, sizeof(char));
        if (list->pathname == NULL)
                return -1;

        list->length = 0;
        list->listsize = LIST_SIZE;

        return 0;
}

/* 释放线性表中各元素占有的存储空间，成功返回0,线性表不存在返回-1
 * 注意：此函数并不释放线性表本身，而只释放由init_list或insert_list申请的空间*/
int destruct_list(PathList *list)
{
        if (list == NULL)
                return -1;
        free(list->pathname);
        list->length = 0;
        list->listsize = 0;
        return 0;
}

/* 在线性表的末尾插入新的entry，成功则返回0,否则返回-1 */
int insert_list(PathList *list, char *entry)
{
        if (list == NULL)
                return -1;

        if (list->length >= list->listsize)
        {
                char (*newbase)[PATH_MAX];
                newbase = realloc(list->pathname, (LIST_SIZE + list->listsize)
                                * PATH_MAX * sizeof(char));
                if (newbase == NULL)
                        return -1;

                list->pathname = newbase;
                list->listsize += LIST_SIZE;
        }

        strncpy(list->pathname[list->length], entry, PATH_MAX);
        ++(list->length);

        return 0;
}

/* 合并两个线性表，将listsrc的内容依次添加到listdest后面，成功返回0,否则返回-1 */
int comb_list(PathList *listdest, PathList *listsrc)
{
        if (listsrc == NULL || listdest == NULL)
                return -1;
        int i;
        for (i = 0; i < listsrc->length; ++i)
                if (insert_list(listdest, listsrc->pathname[i]) < 0)
                        return -1;
        return 0;
}

/* 将pathname目录下的指定类型文件和文件夹绝对路径写入list，不排序，type为文件类型；
 * gethide为显示隐藏文件标志符，0为不显示，否则显示；getrecurv为递归操作标志符，0为不递归，否则递归。
 * 成功返回0，否则返回-1 */
int getentry_list(char *pathname, PathList *list, int type, int gethide,
                int getrecurv)
{
        if (!path_exist(pathname) || file_type(pathname) != TYPE_D || list
                        == NULL)
                return -1;

        DIR *dp;
        struct dirent *dirp;
        char fullpath[PATH_MAX], *ptr;
        int fullpath_type;

        if ((dp = opendir(pathname)) == NULL)
        {
                err_msg("in getentry_list(), opendir error, %s", pathname);
                return -1;
        }

        strncpy(fullpath, pathname, PATH_MAX);
        ptr = fullpath + strlen(fullpath);
        if (ptr[-1] != '/')
                *ptr++ = '/';

        while ((dirp = readdir(dp)) != NULL)
        {
                strcpy(ptr, dirp->d_name);
                fullpath_type = file_type(fullpath);

                if (strncmp(dirp->d_name, ".", 2) != 0 && strncmp(dirp->d_name,
                                "..", 3) != 0)
                {
                        if (dirp->d_name[0] == '.' && gethide == 0)
                                continue;
                        else
                        {
                                if ((fullpath_type & type) == fullpath_type)
                                        insert_list(list, fullpath);
                                if (fullpath_type == TYPE_D && getrecurv)
                                        getentry_list(fullpath, list, type,
                                                        gethide, getrecurv);
                        }
                }
        }
        closedir(dp);

        return 0;
}

/* 返回pathname所指向的文件或文件夹（4096）大小，出错返回-1 */
int64_t get_file_size(char *pathname)
{
        struct stat filebuf;
        if (lstat(pathname, &filebuf) < 0)
        {
                err_msg("in get_file_size(), lstat error, %s", pathname);
                return -1;
        }

        return filebuf.st_size;
}

/* 返回目录中指定文件类型的大小总和，出错返回-1，
 * 按惯例，文件夹大小(每个4K)不应计算在内，但这里并非默认配置，需在type参数传递时指定 */
int64_t get_dir_size(char *pathname, int type, int calhide, int dorecurv)
{
        if (!path_exist(pathname))
                return -1;
        int i;
        int64_t totsize = 0, tmpsize;
        PathList *list = calloc(1, sizeof(PathList));
        init_list(list);

        if (getentry_list(pathname, list, type, calhide, dorecurv) < 0)
                return -1;
        for (i = 0; i < list->length; ++i)
        {
                if ((tmpsize = get_file_size(list->pathname[i])) < 0)
                        return -1;
                else
                        totsize += tmpsize;
        }

        destruct_list(list);
        free(list);

        return totsize;
}

/* 检查是否覆盖，0为不覆盖(skip)，1为覆盖或MERGE，-1为取消(cancel) */
int checkcover(char *pathdest, callback_str *askcover)
{
        int docover;
        if (path_exist(pathdest))
        {
                if (file_type(pathdest) != TYPE_D && ((COVERGFLAG & REPLACEALL)
                                == 0))
                {
                        if (((COVERGFLAG & SKIPALL) == SKIPALL) || !(docover
                                        = askcover(pathdest))) //跳过文件
                        {
                                return 0;
                        }
                        else if (docover == -1) //取消
                                return -1;
                        else
                                return 1; //覆盖
                }
                else if (file_type(pathdest) == TYPE_D && ((COVERGFLAG
                                & MERGEALL) == 0))
                {
                        if (((COVERGFLAG & SKIPALL) == SKIPALL) || !(docover
                                        = askcover(pathdest)))//跳过文件夹
                        {
                                return 0;
                        }
                        else if (docover == -1) //取消
                                return -1;
                        else
                                return 1; //MERGE
                }
        }

        return 1;
}

/* 用于删除pathname，pathname可为文件或目录。成功返回0, 文件不存在返回-1 */
int delpath(char *pathname)
{
        if (!path_exist(pathname))
                return -1;
        else if (file_type(pathname) == TYPE_D && get_dir_size(pathname,
                        TYPE_ALL, 1, 0) > 0)//非空文件夹
        {
                PathList *list = calloc(1, sizeof(PathList));
                init_list(list);
                if (getentry_list(pathname, list, TYPE_ALL, 1, 0) < 0)
                        return -1;

                int i;
                for (i = 0; i < list->length; ++i)
                        delpath(list->pathname[i]);

                destruct_list(list);
                free(list);
        }

        return remove(pathname);/*空文件夹或文件*/
}

/* 文件拷贝，将pathsrc拷贝至pathdest，使用askcover返回覆盖指令，showprogress函数显示拷贝进度
 * 成功则返回文件大小，用户终止返回-1，失败返回-2 */
int64_t copyfile(char *pathsrc, char *pathdest, callback_str *askcover,
                callback_float *showprogress)
{
        if (!path_exist(pathsrc))
        {
                err_msg("in copyfile(), %s", pathsrc);
                return -2;
        }

        int cover_ret;
        int16_t fdsrc, fddest, n;
        float progress = 0.0, prgs_tmp = 0.0;
        char buf[4096];
        int64_t tmpsize = COPIEDSIZE;
        int64_t filesize = get_file_size(pathsrc);

        if (filesize < 0)
                return -2;

        if (path_exist(pathdest))
        {
                cover_ret = checkcover(pathdest, askcover);
                if (cover_ret == -1)
                        return -1;
                else if (cover_ret == 0)
                {
                        COPIEDSIZE += filesize;
                        progress = COPIEDSIZE * 100.0 / TOTSIZE;
                        showprogress(progress);
                        return 0;
                }
                //cover_ret = 1的情况，覆盖，于是这里先将其删除
                if (unlink(pathdest) < 0)
                {
                        err_msg("in copyfile(), %s", pathdest);
                        return -2;
                }
        }

        if ((fdsrc = open(pathsrc, O_RDONLY)) < 0)
        {
                err_msg("in copyfile(), error open, %s", pathsrc);
                return -2;
        }
        if ((fddest = creat(pathdest, 0644)) < 0)
        {
                err_msg("in copyfile(), error open, %s", pathsrc);
                return -2;
        }

        while ((n = read(fdsrc, buf, 4096)) > 0)
        {
                COPIEDSIZE += n;
                progress = COPIEDSIZE * 100.0 / TOTSIZE;
                write(fddest, buf, n);
                if (progress - prgs_tmp >= 0.0001)
                {
                        prgs_tmp = progress;
                        showprogress(progress);
                }
        }
        if (filesize == COPIEDSIZE - tmpsize)
                return filesize;
        else
                return -2;
}

/* 文件夹拷贝，将pathsrc拷贝至pathdest，使用askcover返回覆盖指令，showprogress函数显示拷贝进度
 * 隐藏文件一律拷贝，成功则返回文件夹大小，用户终止返回-1，失败返回-2 */
int64_t copydir(char *pathsrc, char *pathdest, callback_str *askcover,
                callback_float *showprogress)
{
        if (!path_exist(pathsrc))
        {
                err_msg("in copydir(), %s", pathsrc);
                return -2;
        }

        char subdest[PATH_MAX], *srcptr, *destptr;
        int i, cover_ret;
        int64_t dirsize = get_dir_size(pathsrc, TYPE_F, 1, 1);
        int64_t progress;

        cover_ret = checkcover(pathdest, askcover);
        if (cover_ret == -1)
                return -1;
        else if (cover_ret == 0)
        {
                COPIEDSIZE += dirsize;
                progress = COPIEDSIZE * 100.0 / TOTSIZE;
                showprogress(progress);
                return dirsize;
        }

        mkdir(pathdest, 0755);

        PathList *list = calloc(1, sizeof(PathList));
        init_list(list);
        getentry_list(pathsrc, list, TYPE_ALL, 1, 0);

        strncpy(subdest, pathdest, PATH_MAX);
        destptr = subdest + strlen(subdest);
        if (destptr[-1] != '/')
                *destptr++ = '/';

        for (i = 0; i < list->length; ++i)
        {
                if ((COVERGFLAG & CANCELALL) == CANCELALL)
                        break;
                srcptr = list->pathname[i] + strlen(pathsrc);
                if (srcptr[0] == '/')
                        ++srcptr;
                strcpy(destptr, srcptr);
                if (file_type(list->pathname[i]) == TYPE_D)
                {
                        if (copydir(list->pathname[i], subdest, askcover,
                                        showprogress) == -2)
                                return -2;
                }
                else
                {
                        if (copyfile(list->pathname[i], subdest, askcover,
                                        showprogress) == -2)
                                return -2;
                }
        }

        destruct_list(list);
        free(list);
        return dirsize;
}

/* 文件剪切，将pathsrc剪切至pathdest，使用askcover返回覆盖指令，showprogress函数显示拷贝进度
 * 隐藏文件及文件夹一律剪切，成功则返回文件大小，用户终止返回-1，失败返回-2 */
int64_t cutfile(char *pathsrc, char *pathdest, callback_str *askcover,
                callback_float *showprogress)
{
        if (!path_exist(pathsrc))
        {
                err_msg("in cutfile(), %s", pathsrc);
                return -2;
        }

        int64_t filesize = get_file_size(pathsrc);

        float progress = 0.0;
        int cover_ret;
        if ((COVERGFLAG & REPLACEALL) == REPLACEALL)
                cover_ret = 1;
        else
                cover_ret = checkcover(pathdest, askcover);

        if (cover_ret == -1)
                return -1;
        else if (cover_ret == 1)
        {
                unlink(pathdest);
                if (on_same_device(pathsrc, pathdest))
                {
                        if (rename(pathsrc, pathdest) < 0)
                        {
                                err_msg("in cutdir(), rename error, %s",
                                                pathdest);
                                return -2;
                        }
                        printf("通过重命名方式将%s剪切至%s...\n", pathsrc, pathdest);
                        COPIEDSIZE += filesize;
                        progress = COPIEDSIZE * 100.0 / TOTSIZE;
                        showprogress(progress);
                }
                else
                {
                        if (copyfile(pathsrc, pathdest, askcover, showprogress)
                                        != filesize)
                        {
                                err_msg("in cutfile(), copyfile error");
                                return -2;
                        }
                        delpath(pathsrc);
                }
        }
        else
        {
                COPIEDSIZE += filesize;
                progress = COPIEDSIZE * 100.0 / TOTSIZE;
                showprogress(progress);
        }

        return filesize;
}

/* 文件夹剪切，将pathsrc剪切至pathdest，使用askcover返回覆盖指令，showprogress函数显示拷贝进度
 * 成功则返回文件夹大小，用户终止返回-1，失败返回-2 */
int64_t cutdir(char *pathsrc, char *pathdest, callback_str *askcover,
                callback_float *showprogress)
{
        if (!path_exist(pathsrc))
                return -2;

        float progress = 0.0;
        int64_t dirsize = get_dir_size(pathsrc, TYPE_F, 1, 1);
        int cover_ret = checkcover(pathdest, askcover);

        if (cover_ret == 0) //跳过
        {
                COPIEDSIZE += dirsize;
                progress = COPIEDSIZE * 100.0 / TOTSIZE;
                showprogress(progress);
        }
        else if (cover_ret == 1)//MERGE
        {
                char subdest[PATH_MAX], *srcptr, *destptr;
                int i;

                mkdir(pathdest, 0755);
                PathList *list = calloc(1, sizeof(PathList));
                init_list(list);
                getentry_list(pathsrc, list, TYPE_ALL, 1, 0);

                strncpy(subdest, pathdest, PATH_MAX);
                destptr = subdest + strlen(subdest);
                if (destptr[-1] != '/')
                        *destptr++ = '/';

                for (i = 0; i < list->length; ++i)
                {
                        if ((COVERGFLAG & CANCELALL) == CANCELALL)
                                break;
                        srcptr = list->pathname[i] + strlen(pathsrc);
                        if (srcptr[0] == '/')
                                ++srcptr;
                        strcpy(destptr, srcptr);
                        if (file_type(list->pathname[i]) == TYPE_D)
                        {
                                if (cutdir(list->pathname[i], subdest,
                                                askcover, showprogress) == -2)
                                        return -2;
                        }
                        else
                        {
                                if (cutfile(list->pathname[i], subdest,
                                                askcover, showprogress) == -2)
                                        return -2;
                        }

                }

                destruct_list(list);
                free(list);
                remove(pathsrc);
        }
        else
                return -1;
        return dirsize;
}

/* 文件管理模块外部接口，对list中的各文件和文件夹作act处理，act分ACT_DEL、ACT_CUT、ACT_CPY
 * 三种，分别为删除，剪切，复制。成功返回0，失败返回-1 */
int dolist(PathList *list, char *pathdest, int act, callback_str *askcover,
                callback_float *showprogress)
{
        int i;
        char fullpath[PATH_MAX], *ptr, *fullptr;

        TOTSIZE = 0;
        for (i = 0; i < list->length; ++i)
        {
                if (file_type(list->pathname[i]) == TYPE_D)
                        TOTSIZE
                                        += get_dir_size(list->pathname[i],
                                                        TYPE_F, 1, 1);
                else
                        TOTSIZE += get_file_size(list->pathname[i]);
        }

        strncpy(fullpath, pathdest, PATH_MAX);
        fullptr = fullpath + strlen(fullpath);

        if (pathvalid(list, pathdest, act) != 1)
                return -1;

        switch (act)
        {
        case ACT_DEL:
                for (i = 0; i < list->length; ++i)
                        delpath(list->pathname[i]);
                break;
        case ACT_CUT:
                for (i = 0; i < list->length; ++i)
                {
                        ptr = strrchr(list->pathname[i], '/');
                        strcpy(fullptr, ptr);
                        if (file_type(list->pathname[i]) == TYPE_D)
                        {
                                if (cutdir(list->pathname[i], fullpath,
                                                askcover, showprogress) == -2)
                                        return -1;
                        }
                        else
                        {
                                if (cutfile(list->pathname[i], fullpath,
                                                askcover, showprogress) == -2)
                                        return -1;
                        }
                }
                break;
        case ACT_CPY:
                for (i = 0; i < list->length; ++i)
                {
                        ptr = strrchr(list->pathname[i], '/');
                        strcpy(fullptr, ptr);
                        if (file_type(list->pathname[i]) == TYPE_D)
                        {
                                if (copydir(list->pathname[i], fullpath,
                                                askcover, showprogress) == -2)
                                        return -1;

                        }
                        else
                        {
                                if (copyfile(list->pathname[i], fullpath,
                                                askcover, showprogress) == -2)
                                        return -1;
                        }
                }
                break;
        default:
                return -1;
        }
        return 0;
}

/* 获取当前工作目录下的文件夹文件夹，文件夹在前，文件在后，按拼音排序 */
int get_cwd_entry(PathList *list)
{
        char cwd[PATH_MAX];
        if (getcwd(cwd, PATH_MAX) == NULL)
                return -1;

        PathList *flist = calloc(1, sizeof(PathList));
        init_list(flist);
        setlocale(LC_ALL, "zh_CN.utf8");

        if (getentry_list(cwd, list, TYPE_D, SHOWHIDE, 0) < 0)
                return -1;
        qsort(list->pathname, list->length, PATH_MAX, (__compar_fn_t) strcoll);

        if (getentry_list(cwd, flist, TYPE_F, SHOWHIDE, 0) < 0)
                return -1;
        qsort(flist->pathname, flist->length, PATH_MAX, (__compar_fn_t) strcoll);

        comb_list(list, flist);

        destruct_list(flist);
        free(flist);

        return 0;
}
